/**
 * @Description file文件缓存
 * @Author: colpu
 * @Date: 2018-09-23 17:37:04
 * @Last Modified by: mikey.zhaopeng
 * @Last Modified time: 2023-08-06 09:50:35
 */
const fileUtils = require("@colpu/core/src/utils/file");
const fs = require("fs");
const path = require("path");
const qs = require("querystring");
module.exports = (app) => {
  const { config } = app;
  return async (ctx, next) => {
    if (app.env == "development") {
      return next();
    }
    let query = ctx.query;
    const fileCacheConf = config.fileCache;
    let {
      cacheTime = 1800,
      isCache = false,
      root = "dist/filecache",
    } = fileCacheConf || {};

    /**
     * 缓存机制:
     * 1. 以环境默认配置为优先配置
     * 2. 默认不传cache, 或为true, 或为1都是要缓存，由于query后得到的是字符串，这里做特殊处理
     */
    const queryCache =
      !query.cache || query.cache === "true" || query.cache === "1";
    if (isCache && queryCache) {
      const refresh = query.refresh;
      const requestPath = getRequestPath(ctx, fileCacheConf);
      // 从OSS上去获取文件，如果获取到文件则输出文件
      let fileData,
        filePath = root + requestPath,
        stat;

      // 获取文件是否存在
      try {
        stat = fs.statSync(filePath);
      } catch (err) {
        stat = false;
      }

      if (stat) {
        fileData = fileUtils.readFileSync(filePath, {
          encoding: "utf8",
        });
      }

      // 定义内容
      let body;
      if (fileData && stat) {
        const lastModified = new Date(stat.mtime).getTime();
        if (lastModified + cacheTime * 1000 > Date.now()) {
          body = fileData;
        }
      }

      // 设置文件缓存头标识
      const setHeader = (ctx) => {
        ctx.set({
          "X-Is-Cache": true,
          "X-Cache-Time": Date.now(),
          "Content-Type": "application/json;charset=utf-8",
        });
      };

      // 是否刷新文件缓存，
      // 默认不存在refresh不刷新文件，存在则跳过以下判断
      // 则会走入中间件，当中间件执行后，此时为最新数据html这时
      // 将文件写入到磁盘，如果这时报错会显示老的文件缓存文件（如果存在）
      if (body && !refresh) {
        setHeader(ctx);
        ctx.body = body;
        return;
      }
      // 如果是存在缓存时出现错误
      try {
        await next();
      } catch (error) {
        // 错误时将缓存文件输出
        if (fileData && stat) {
          setHeader(ctx);
          ctx.body = fileData;
          ctx.status = 200;
        } else {
          throw error;
        }
      }
      // 如果是开发环境则不保存body
      body = ctx.body;
      if (ctx.status === 200 && body) {
        if (!stat) {
          fileUtils.mkdirsSync(path.dirname(filePath));
        }
        const bodyString =
          typeof body === "string" ? body : JSON.stringify(body);
        // 模板缓存时长为永久保存
        fs.writeFile(filePath, bodyString, (err) => {
          if (err) {
            console.error(`缓存文件${filePath}保存失败!`, err);
            return;
          }
          console.info("文件已被保存为缓存");
        });
      }
    } else {
      await next();
    }
  };
};

function getRequestPath(ctx, options) {
  // 处理路径
  let requestPath = ctx.path;
  const extname = path.extname(requestPath);
  const suffix = extname || options.suffix || "html";
  requestPath = extname
    ? requestPath
    : `${requestPath}${
        requestPath.substr(-1) === "/" ? "" : "/"
      }index.${suffix}`;
  if (options.cacheQuery) {
    delete ctx.query.refresh;
    delete ctx.query.cache;
    if (qs.stringify(ctx.query)) {
      requestPath = requestPath.replace(
        "." + suffix,
        `_${fileUtils.createHash(qs.stringify(ctx.query), 16)}.${suffix}`
      );
    }
  }
  return requestPath;
}
